const { Controller } = require("egg");
const crypto = require("crypto")
class OrderController extends Controller {
  // 创建订单（不需要微信支付模拟下单调用）
  async createOrder() {
    const { ctx, service } = this;
    const { remark, payMethod, orderType, addressId, goods, phone, openid } =
      ctx.request.body;
    ctx.validate({
      payMethod: { type: "nullValue", fieldName: "payMethod" },
      orderType: { type: "nullValue", fieldName: "orderType" },
      goods: { type: "nullValue", fieldName: "goods" },
      phone: { type: "nullValue", fieldName: "phone" },
      openid: { type: "nullValue", fieldName: "openid" },
    });
    if (orderType === 1) {
      ctx.validate({
        addressId: { type: "nullValue", fieldName: "addressId" },
      });
    }
    const res = await service.order.createOrder({
      userId: ctx.auth._id,
      remark,
      payMethod,
      orderType,
      addressId,
      goods,
      phone,
      openid,
      out_trade_no: ctx.generateOutTradeNo()
    });
    ctx.send(res);
  }
  // 获取订单列表（小程序与商家通用）
  async getOrderList() {
    const { ctx } = this;
    const { page = 1, size = 20, type } = ctx.request.query;
    // type=0,小程序；type=1，商家
    const condition = type ? {} : { userId: ctx.auth._id };
    const res = await ctx.model.Order.find(
      condition,
      {},
      { limit: size, skip: (page - 1) * size, sort: { createdAt: -1 } }
    );
    const total = await ctx.model.Order.countDocuments();
    ctx.send({ msg: "成功", data: { list: res, total, page, size } });
  }
  //   获取订单详情（小程序与商家通用）
  async getOrderDetail() {
    const { ctx, service } = this;
    const { _id } = ctx.request.query;
    const res = await service.order.getOrderDetail(_id);
    ctx.send({ msg: "成功", data: res });
  }
  // 删除订单
  async delOrder() {
    const { ctx } = this;
    const { _id } = ctx.params;
    const res = await ctx.model.Order.deleteOne({ _id });
    ctx.send({ msg: "成功" });
  }
  // 获取订单的商品
  async getOrderGoods() {
    const { ctx } = this;
    const { _id } = ctx.request.query;
    const res = await ctx.model.Order.findById(
      { _id },
      { "goods.goodsId": 1, "goods.image": 1, "goods.name": 1 }
    );
    ctx.send({ msg: "成功", data: res });
  }
  // 评价订单
  async evalOrder() {
    const { ctx, service } = this;
    const { orderId, goods, admin } = ctx.request.body;
    ctx.validate({
      orderId: { type: "nullValue", fieldName: "orderId" },
    });
    await service.goodsEval.evalGoods({ userId: ctx.auth._id, orderId, goods });
    await ctx.model.AdminEval.create({
      userId: ctx.auth._id,
      orderId,
      content: admin.content,
      imgFiles: admin.imgFiles,
      shopRate: admin.shopRate,
      envRate: admin.envRate,
      serviceRate: admin.serviceRate,
    });
    ctx.send({ msg: "成功" });
  }
  // 微信支付
  async wxPay() {
    const { ctx, service } = this;
    const { remark, payMethod, orderType, addressId, goods, phone, openid } =
      ctx.request.body;
    console.log(ctx.request.body);
    ctx.validate({
      payMethod: { type: "nullValue", fieldName: "payMethod" },
      orderType: { type: "nullValue", fieldName: "orderType" },
      goods: { type: "nullValue", fieldName: "goods" },
      phone: { type: "nullValue", fieldName: "phone" },
      openid: { type: "nullValue", fieldName: "openid" },
    });
    if (orderType === 1) {
      ctx.validate({
        addressId: { type: "nullValue", fieldName: "addressId" },
      });
    }
    const res = await service.order.wxPay({
      userId: ctx.auth._id,
      remark,
      payMethod,
      orderType,
      addressId,
      goods,
      phone,
      openid,
      out_trade_no: ctx.generateOutTradeNo()
    });

    ctx.send(res)
  }

  // 微信支付成功的回调
  async notify_url(_id) {
    const { ctx, app } = this;
    const { event_type, resource } = ctx.request.body;
    if (event_type === "TRANSACTION.SUCCESS") {
      this.status = 200;//成功关闭通知

      /******************  对支付结果解密  *****************/
      const key = Buffer.from(app.config.wx.wxKeyV3, 'utf8');//转二进制
      const nonce = Buffer.from(resource.nonce, 'utf8');
      const associated_data = Buffer.from(resource.associated_data, 'utf8');
      const ciphertext = Buffer.from(resource.ciphertext, 'base64');

      // associated_data小于16个字节，取最后16字节作为认证标签
      const authTag = Buffer.from(ciphertext.subarray(ciphertext.length - 16))

      // 创建AEAD_AES_256_GCM解密器对象
      const decipher = crypto.createDecipheriv("aes-128-gcm", key, nonce)
      //设置关联数据，与密文进行身份验证
      decipher.setAAD(associated_data);
      // 设置认证标签
      decipher.setAuthTag(authTag);
      // 取16个字节以外的所有字节作为加密数据
      const encryptedData = Buffer.from(ciphertext.subarray(0, ciphertext.length - 16))
      // 使用update对加密数据解密
      let decrypted = decipher.update(encryptedData)
      // 指定编码格式，解密器16位数据+剩余解密数据
      decrypted += decipher.final('utf8')
      console.log(decrypted);
      const payData = JSON.parse(decrypted)

      /**************************支付成功修改库存，销量，订单推送等 */
      const db = ctx.model.Order;
      const orderData = await db.findById(payData.attach);
      // 判断该订单是否已支付成功（可能订单会多次推）
      if (orderData.payMethod != 1) {//待支付
        await db.updateOne({ _id: payData.attach }, { payMethod: 1 })
        // 自提生成取餐码
        if (orderData.orderType === 0) {
          const lastOrder = await db
            .find({ orderType: 0 }, {}, { sort: "-takeMealCode" })
            .limit(1);
          await db.updateOne(
            { _id: payData.attach },
            { takeMealCode: lastOrder[0].takeMealCode + 1 }
          );
        }
        // 库存自减，销量自增
        const skuList = ctx.model.Attrdetail;
        const goods = ctx.model.Goods;
        for (const item of orderData.goods) {
          if (item.sid?.length) {
            await skuList.updateOne(
              { sid: item.sid },
              { $inc: { stock: -item.quantity } }
            );
          }
          await goods.updateOne(
            { _id: item.goodsId },
            { $inc: { stock: -item.quantity, saleNum: item.quantity } }
          );
        }
        // 推送订单通知
        app.io.emit("orderInform", 0)
      }
    }

  }
}

module.exports = OrderController;
